// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-08-07

package base

import (
	"context"
	"errors"
	"fmt"
	"html/template"
	"os"
	"regexp"
)

// Target
// is a type name for generated file path.
//
// Example:
//
//	Target("/data/sketch/app/models/example.go")
//	Target("/data/sketch/app/services/example_service.go")
type Target string

// Can
// return true if target file can be saved.
func (o Target) Can(override bool) (bool, error) {
	// Return true
	// if override enabled.
	if override {
		return true, nil
	}

	// Stat
	// target file.
	_, err := os.Stat(o.String())

	// Return false
	// if target file is existed.
	if err == nil {
		return false, nil
	}

	// Return true
	// if target file is not exists.
	if errors.Is(err, os.ErrNotExist) {
		return true, nil
	}

	// Unknown
	// error.
	return false, err
}

// Save
// template text to target file.
func (o Target) Save(_ context.Context, text string, result any) (err error) {
	var (
		file   *os.File
		info   os.FileInfo
		target = o.String()
		tpl    *template.Template
	)

	// Make directory.
	if path := regexp.MustCompile(`/([^/]+)$`).ReplaceAllString(target, ""); path != target {
		if info, err = os.Stat(path); err != nil {
			if errors.Is(err, os.ErrNotExist) {
				err = os.MkdirAll(path, os.ModePerm)
			} else {
				return
			}
		} else if !info.IsDir() {
			err = fmt.Errorf(`invalid directory`)
			return
		}
	}

	// Open
	// a file, Create new file if not exists.
	if file, err = os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm); err != nil {
		return
	}

	// Close
	// file resource when done.
	defer func() { _ = file.Close() }()

	// Execute
	// template variables with given data.
	if tpl, err = template.New("template").Parse(text); err == nil {
		err = tpl.Execute(file, result)
	}
	return
}

// String
// returns a path string.
//
// Output:
//
//	/data/sketch/app/models/example.go
//	/data/sketch/app/services/example_service.go
func (o Target) String() string {
	return string(o)
}
